/*
 * graph2D
 * Copyright (c) 2013 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#if !defined(___GRAPH2D_OBJECT_H___)
#define ___GRAPH2D_OBJECT_H___

#if !defined(___GRAPH2D_COMMON_H___)
#include "common.h"
#endif

#if !defined(___GRAPH2D_COLOR_H___)
#include "color.h"
#endif

#if !defined(___GRAPH2D_GRAPHIC_DEVICE_H___)
#include "graphicDevice.h"
#endif

#if !defined(___GRAPHC2D_ACTION_H___)
#include "action.h"
#endif

#if !defined(___GRAPHC2D_ANIMATIN_ACTION_H___)
#include "animationAction.h"
#endif

#include <string>

namespace Graph2D
{
	class Texture;

	////////////////////////////////////////////////////////////////////////////////////////////////
	static const int TYPE_OBJECT				= (0x00000001);							//!< Object
	static const int TYPE_TEXT					= (0x00000004|TYPE_OBJECT);				//!< Text
	static const int TYPE_COMPONENT				= (0x00000008|TYPE_OBJECT);				//!< Component
	static const int TYPE_IMAGE_BUTTON			= (0x00000010|TYPE_COMPONENT);			//!< ImageButton
	static const int TYPE_BUTTON				= (0x00000020|TYPE_IMAGE_BUTTON);		//!< Button
	static const int TYPE_SCROLL_BAR			= (0x00000040|TYPE_COMPONENT);			//!< ScrollBar
	static const int TYPE_CONTAINER				= (0x00000080|TYPE_COMPONENT);			//!< Container
	static const int TYPE_STATIC_CONTAINER		= (0x00000100|TYPE_CONTAINER);			//!< StaticContainer
	static const int TYPE_VARIABLE_CONTAINER	= (0x00000200|TYPE_CONTAINER);			//!< VariableContainer
	static const int TYPE_MESSAGE				= (0x00000400|TYPE_STATIC_CONTAINER);	//!< Message
	static const int TYPE_SCENE					= (0x00000800|TYPE_VARIABLE_CONTAINER);	//!< Scene
	static const int TYPE_WINDOW				= (0x00001000|TYPE_CONTAINER);			//!< Window
	static const int TYPE_STATIC_WINDOW			= (0x00002000|TYPE_WINDOW|TYPE_STATIC_CONTAINER);	//!< Window
	static const int TYPE_VARIABLE_WINDOW		= (0x00004000|TYPE_WINDOW|TYPE_VARIABLE_CONTAINER);	//!< Window
	static const int TYPE_MENU					= (0x00008000|TYPE_STATIC_WINDOW);		//!< Menu
	static const int TYPE_DIALOG				= (0x00010000|TYPE_MENU);				//!< Dialog
	static const int TYPE_MESSAGE_WINDOW		= (0x00020000|TYPE_STATIC_WINDOW);		//!< MessageWindow
	static const int TYPE_TEXT_WINDOW			= (0x00040000|TYPE_STATIC_WINDOW);		//!< TextWindow
#define TYPE_CUSTOM				= (0x00080000);							//!< ユーザー定義

	////////////////////////////////////////////////////////////////////////////////////////////////
#define COMPONENT_FLAGS_VISIBLE_BIT				(0x01)	//!< 表示するのか？
#define COMPONENT_FLAGS_ENABLE_BIT				(0x02)	//!< 有効？
#define COMPONENT_FLAGS_CLIPPING_BIT			(0x04)	//!< クリッピングする？
#define COMPONENT_FLAGS_OPAQUE_BIT				(0x08)	//!< 不透明？
#define COMPONENT_FLAGS_TOUCHABLE_BIT			(0x10)	//!< タッチ可能？
#define COMPONENT_FLAGS_SCALE_ANIMATION_BIT		(0x20)	//!< スケールアニメーション
#define COMPONENT_FLAGS_COLOR_ANIMATION_BIT		(0x40)	//!< カラーアニメーション
#define COMPONENT_FLAGS_OWNERDRAW_BIT			(0x80)	//!< 自身で描画する？

	////////////////////////////////////////////////////////////////////////////////////////////////
	//! @brief テキストクラス
	class Object
	{
		DISALLOW_COPY_AND_ASSIGN(Object);

		friend class MoveAction;
		friend class MoveOffsetAction;
		friend class ScaleAction;
		friend class ColorAction;

	public:
		Object();
		virtual ~Object();

		////////////////////////////////////////////////////////////////////////////////////////////
		/*!
		 * タイプ名を取得します
		 * @return	タイプ名
		 */
		virtual const char* getTypeName() const
		{
			return "Object";
		}

		/*!
		 * タイプ識別子を取得します
		 * @return	タイプ識別子
		 */
		unsigned int getType() const
		{
			return type;
		}

		/*!
		 * タイプが一致するか判定します
		 * @param[in]	type	判定するタイプ識別子
		 * @retval		true	一致
		 * @retval		false	不一致
		 */
		bool isA(const unsigned int type) const
		{
			return this->type == type;
		}

		/*!
		 * タイプが同系統か判定します
		 * @param[in]	type	判定するタイプ識別子
		 * @retval		true	同系統
		 * @retval		false	違う系統
		 */
		bool isKindOfA(const unsigned int type) const
		{
			return (this->type & type) == type;
		}

		/*!
		 * オブジェクトＩＤを取得します
		 * @return	オブジェクトＩＤ
		 */
		unsigned int getID() const
		{
			return identification;
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		Vector2 getPosition() const;					//!< ローカル座標を取得します
		void setPosition(const Vector2& position);		//!< ローカル座標を設定します
		void setPosition(const float x, const float y);	//!< ローカル座標を設定します

		const Vector2& getSize() const;					//!< サイズを取得します
		void setSize(const Vector2& size);				//!< サイズを設定します
		void setSize(const float w, const float h);		//!< サイズを設定します

		const Vector2& getScale() const;								//!< スケールを取得します
		void setScale(const Vector2& scale, const float second);		//!< スケールを設定します
		void setScale(const float x, const float y, const float second);//!< スケールを設定します

		const Color& getColor() const;							//!< カラーを取得します
		void setColor(const Color& color, const float second);	//!< カラーを設定します
		void setColor(const float r, const float g, const float b, const float a, const float second);	//!< カラーを設定します

		/*!
		 * 指定座標を含むか否か
		 * @param[in]	localPosition	ローカル座標
		 * @retval		true			領域内の点
		 * @retval		false			領域外の点
		 */
		virtual bool isInPosition(const Vector2& localPosition) const
		{
			const float w = size.x;
			const float h = size.y;
			return (localPosition.x >= 0) && (localPosition.y >= 0) && (localPosition.x < w) && (localPosition.y < h);
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		GraphicDevice::BlendMode getBlendMode() const;				//!< ブレンドモードを取得します
		void setBlendMode(const GraphicDevice::BlendMode blendMode);//!< ブレンドモードを設定します

		////////////////////////////////////////////////////////////////////////////////////////////
		bool getOpaque() const;							//!< 不透明状態を取得します
		void setOpaque(const bool opaque);				//!< 不透明状態を設定します

		bool getClipping() const;						//!< シザリング状態を取得します
		void setClipping(const bool clippint);			//!< シザリング状態を設定します

		bool getVisible() const;						//!< 表示するか取得
		void setVisible(const bool visible);			//!< 表示するか設定

		bool getEnable() const;							//!< 有効・無効設定
		void setEnable(const bool enable);				//!< 有効か取得

		bool getTouchable() const;						//!< タッチ可能状態を取得します
		void setTouchable(const bool touchable);		//!< タッチ可能状態を設定します

		bool getOwnerDraw() const;						//!< オーナードローフラグ取得
		void setOwnerDraw(const bool ownerDraw);		//!< オーナードローフラグ設定

		unsigned getUserData() const;					//!< ユーザーデータを取得します
		void setUserData(const unsigned data);			//!< ユーザーデータを設定します


		////////////////////////////////////////////////////////////////////////////////////////////
		bool loadTexture(const std::string& filename);	//!< テクスチャを読み込みます
		void unloadTexture();							//!< テクスチャを開放します
		Texture* getTexture() const;					//!< テクスチャを取得します
		void setTexture(Texture* texture);				//!< テクスチャを設定します
		void bindTexture();

		const Vector2& getTexturePosition() const;				//!< テクスチャ貼り付け座標を取得します
		void setTexturePosition(const Vector2& position);		//!< テクスチャ貼り付け座標を設定します
		void setTexturePosition(const float x, const float y);	//!< テクスチャ貼り付け座標を設定します

		const Vector2& getTextureSize() const;					//!< テクスチャ貼り付け座標を取得します
		void setTextureSize(const Vector2& size);				//!< テクスチャ貼り付け座標を設定します
		void setTextureSize(const float w, const float h);		//!< テクスチャ貼り付け座標を設定します

		////////////////////////////////////////////////////////////////////////////////////////////
#if 0
		void setTextureScroll(const Vector2& speed);	//!< テクスチャスクロールスピードを設定します


		void setCellAnimation(const Vector2& position, const Vector2& size, const float wait, const unsigned short count, const short loopCount);
		void setCellAnimation(const float x, const float y, const float w, const float h, const float wait, const unsigned short count, const short loopCount)
		{
			setCellAnimation(Vector2(x, y), Vector2(w, h), wait, count, loopCount);
		}
		void clearCellAnimation()						//!< アトラスアニメーションを消去します
		{
			animationCellEnable = false;
		}

		void startCellAnimation()						//!< アトラスアニメーションを開始します
		{
			animationCellTime = 0;
			animationCellCount = 0;
			animationCellLoopCount = animationCellLoopCountOrigin;
			animationCellRunning = true;
		}

		void stopCellAnimation()						//!< アトラスアニメーションを停止します
		{
			animationCellRunning = false;
		}

		float getCellAnimationTime() const;

		void setCellNumber(int pattern)					//!< アトラスを指定します
		{
			if(pattern < 0)
				pattern = 0;
			/*
			else if(pattern > animationCellMaxCount)
				pattern = animationCellMaxCount;
			*/
			animationCellCount = static_cast<unsigned short>(pattern);
		}

		void setCell(const Vector2& position, const Vector2& size);
		void setCell(const float x, const float y, const Vector2& size)
		{
			setCell(Vector2(x, y), size);
		}
		void setCell(const float x, const float y, const float w, const float h)
		{
			setCell(Vector2(x, y), Vector2(w, h));
		}

		bool isRunningCellAnimation() const
		{
			return animationCellRunning;
		}

		void addAnimation(const std::string& name, CellAnimation* animation)
		{
			animations.insert(std::pair<std::string, CellAnimation*>(name, static_cast<CellAnimation*>(animation->retain())));
		}

		void removeAnimation(const std::string& name);	//!< アニメーションを消去します
		bool setAnimation(const std::string& name);		//!< アニメーションを開始します
		CellAnimation* getAnimation(const std::string& name);	//!< アニメーションを取得します

#endif

		////////////////////////////////////////////////////////////////////////////////////////////
		void move(const Vector2& position, const float second);			//!< 移動します
		void move(const float x, const float y, const float second);	//!< 移動します
		void chase(const Vector2& position, const float rate);			//!< 移動します
		void chase(const float x, const float y, const float rate);		//!< 移動します
		bool arrived() const;											//!< 到着したか調べます

		void jump(const float height, const float second);				//!< 垂直ジャンプします
		void shake(const float amplitude, const float second);			//!< 振動します

		void setAnimation(AnimationAction* animationAction);
		void setAnimation(const size_t patternCount, const Animation* animation, const bool loop);

		////////////////////////////////////////////////////////////////////////////////////////////
		virtual void onUpdate(const UpdateInfomation& updateInfomation);//!< 更新イベント
		virtual bool onDraw(const DrawRect& drawRect);					//!< 描画イベント

	protected:
		/*!
		 * タイプ識別子を設定します
		 * @param[in]	type	オブジェクトのタイプ識別子
		 */
		void setType(unsigned int type)
		{
			this->type = type;
		}
		virtual Color getDrawColor(const DrawRect& drawRect) const;
		virtual Color getDrawColor(const DrawRect& drawRect, const Color& color) const;

	private:
		unsigned int type;						//!< コンポーネントタイプ
		unsigned int identification;			//!< ユニーク番号

		Texture* texture;						//!< テクスチャ
		unsigned data;							//!< 関連付けたデータ
		Vector2 position;						//!< 位置
		Vector2 offset;							//!< オフセット位置
		Vector2 size;							//!< サイズ
		Vector2 scale;							//!< スケール
		Color color;							//!< 色
		Vector2 texturePosition;				//!< テクスチャ貼り付け位置
		Vector2 textureSize;					//!< テクスチャ貼り付けサイズ

		unsigned char flags;					//!< 状態フラグ
		unsigned char translationAnimationType;	//!< 移動アニメーションタイプ
		unsigned char blendMode;				//!< ブレンドモード
		unsigned char padding[2];

		Action* actions[Action::TYPE_SIZE];

		//TODO:あったら良さそうな機能：回転アニメーション（シザリングどうする？）
		//TODO:あったら良さそうな機能：フィルター系エフェクト
		//TODO:基本クラスが大きくなりすぎているので分割しましょう
	};
}

#endif
